package Merlion.FRM.bean;

import Merlion.FRM.entity.InvoiceAndReceiptEntity;
import Merlion.FRM.entity.CreditProfileEntity;
import Merlion.CRM.entity.ClientEntity;
import Merlion.CRM.entity.LineEntryEntity;
import Merlion.CRM.entity.SalesOrderEntity;
import Merlion.FRM.entity.InvoiceLineEntryEntity;
import Merlion.util.state.InvoiceAndReceiptState;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
public class InvoiceAndReceiptBean implements InvoiceAndReceiptBeanRemote {

    @PersistenceContext
    private EntityManager em;
    @EJB
    private PaymentBeanRemote paymentBean;

    public void doGenerateDunningLetter(Long invoiceId) {
        System.out.println("**InvoiceAndReceiptBean : doGenerateDunningLetter()");
        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Long salesOrderId = invoice.getSalesOrderId();
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);

        Long customerId = salesOrder.getClient().getId();

        Query q = em.createQuery("SELECT c FROM CreditProfileEntity c WHERE c.customerId = :customerId");
        q.setParameter("customerId", customerId);
        CreditProfileEntity creditProfile = (CreditProfileEntity) q.getSingleResult();
        int defaults = creditProfile.getDefaults() + 1;
        creditProfile.setDefaults(defaults);
        System.out.println("**InvoiceAndReceiptBean : payment default has been increased");

        //PRINT DUNNING LETTER            
    }

    public Long addInvoice(Long salesOrderId, Date issueDate, Date invoiceDueDate, int discountedCreditTerm, String paymentMethod) {
        System.out.println("**InvoiceAndReceiptBean: addInvoice()");
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        Collection<LineEntryEntity> lineEntryList = salesOrder.getLineEntryList();
        Long invoiceId = null;

        try {
            InvoiceAndReceiptEntity invoice = new InvoiceAndReceiptEntity();

            //get today's date 
            SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            Calendar calendar = Calendar.getInstance();
            String isDate = dateFormat.format(calendar.getTime());
            Date curDate = (Date) dateFormat.parse(isDate); //parse to "Date" format

            if (salesOrder != null) {
                ClientEntity client = salesOrder.getClient();
                Long customerId = client.getId();

                Query q = em.createQuery("SELECT c FROM CreditProfileEntity c WHERE c.customerId = :customerId");
                q.setParameter("customerId", customerId);
                CreditProfileEntity creditProfile = (CreditProfileEntity) q.getSingleResult();

                //create line entries for products invoiced
                Collection<InvoiceLineEntryEntity> iLineEntryList = new ArrayList();
                for (Object o : lineEntryList) {
                    LineEntryEntity line = (LineEntryEntity) o;

                    InvoiceLineEntryEntity newILine = new InvoiceLineEntryEntity();

                    newILine.setItemId(line.getItemId());
                    newILine.setItemName(line.getItemName());
                    newILine.setItemQuantity(line.getAmountInvoiced());
                    newILine.setListPrice(line.getPrice());
                    newILine.setTotalLinePrice(line.getPrice() * line.getAmountInvoiced());//assuming flat pricing policy
                    newILine.setDeleteFlag(false);
                    em.persist(newILine);
                    em.flush();

                    iLineEntryList.add(newILine);
                }

                //set discount rate standard based on customer grade
                Double discountRate = 0.0;
                if (creditProfile.getCustomerGrade().equalsIgnoreCase("A")) {
                    discountRate = 5.0;
                } else if (creditProfile.getCustomerGrade().equalsIgnoreCase("B")) {
                    discountRate = 3.0;
                } else if (creditProfile.getCustomerGrade().equalsIgnoreCase("C")) {
                    discountRate = 2.0;
                } else {
                    discountRate = 0.0;
                }

                //count total price from line entry list of given sales order
                Double totalPrice = 0.0;

                if (salesOrder.getPricingType().equalsIgnoreCase("F")) {
                    for (Object o : lineEntryList) {
                        LineEntryEntity l = (LineEntryEntity) o;
                        totalPrice = totalPrice + (l.getAmountInvoiced() * l.getPrice());
                        l.setAmountShipped(l.getAmountShipped() + l.getAmountInvoiced());
                        l.setAmountInvoiced(0);
                        em.merge(l);
                        em.flush();
                    }
                } else if (salesOrder.getPricingType().equalsIgnoreCase("D")) {
                    for (Object o : lineEntryList) {
                        LineEntryEntity l = (LineEntryEntity) o;
                        totalPrice = totalPrice + (l.getAmountInvoiced() * l.getPrice());
                        l.setAmountShipped(l.getAmountShipped() + l.getAmountInvoiced());
                        l.setAmountInvoiced(0);
                        em.merge(l);
                        em.flush();
                    }
                }

                //count amount due after considering discount rate
                Double amountDue = 0.0;
                amountDue = totalPrice * ((100 - discountRate) / 100);

                invoice.setSalesOrderId(salesOrderId);
                invoice.setDiscountRate(discountRate);
                invoice.setTotalPrice(totalPrice);
                invoice.setIssueDate(issueDate);
                invoice.setInvoiceDueDate(invoiceDueDate);
                invoice.setCreditTerm(30); //default credit term is 30 days
                invoice.setDiscountedCreditTerm(discountedCreditTerm);
                invoice.setAmountDue(amountDue);
                if (salesOrder.getPaymentMethod().equalsIgnoreCase("Cash")) {
                    invoice.setCashInAdvance(salesOrder.getTotalPrice());
                    invoice.setInvoiceStatus(true);
                } else {
                    invoice.setCashInAdvance(0.00);
                    invoice.setInvoiceStatus(false);
                }
                invoice.setPaymentMethod(paymentMethod);
                invoice.setDeleteFlag("N");
                invoice.setiLineEntry(iLineEntryList);
                em.persist(invoice);
                em.flush();

                invoiceId = invoice.getInvoiceId();
                if (salesOrder.getPaymentMethod().equalsIgnoreCase("Cash")) {
                    paymentBean.recordPaymentJournal(invoiceId, totalPrice);
                } else {
                }
                System.out.println("**InvoiceAndReceiptBean: New invoice has been successfully created.");
                return invoiceId;
            } else {
                System.out.println("**InvoiceAndReceiptBean: Fail to create new invoice because sales order id does not exist");
                return null;
            }
        } catch (Exception e) {
            System.out.println("**InvoiceAndReceiptBean: Fail to create new invoice because " + e.getMessage().toString());
            return null;
        }
    }

    public void editInvoice(Long invoiceId, Long salesOrderId, Double cashInAdvance, Double discountRate, Double totalPrice,
            Date issueDate, Date invoiceDueDate, int creditTerm, int discountedCreditTerm,
            Double amountDue, boolean invoiceStatus, String deleteFlag) {

        System.out.println("**InvoiceAndReceiptBean: editInvoice()");
        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);

        try {
            if (invoice != null && invoice.getDeleteFlag().equalsIgnoreCase("N")) {
                invoice.setSalesOrderId(salesOrderId);
                invoice.setCashInAdvance(cashInAdvance);
                invoice.setDiscountRate(discountRate);
                invoice.setTotalPrice(totalPrice);
                invoice.setIssueDate(issueDate);
                invoice.setInvoiceDueDate(invoiceDueDate);
                invoice.setCreditTerm(creditTerm);
                invoice.setDiscountedCreditTerm(discountedCreditTerm);
                invoice.setAmountDue(amountDue);
                invoice.setInvoiceStatus(invoiceStatus);
                invoice.setDeleteFlag(deleteFlag);
                em.merge(invoice);
                em.flush();

                System.out.println("**InvoiceAndReceiptBean: Invoice ID: " + invoice.getInvoiceId() + " successfully edited.");
            } else {
                System.out.println("**InvoiceAndReceiptBean: Invoice does not exist.");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void deleteInvoice(Long invoiceId) {
        System.out.println("**InvoiceAndReceiptBean: deleteInvoice()");
        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);

        try {
            if (invoice == null) {
                System.out.println("**InvoiceAndReceiptBean: Invoice does not exist.");
            } else if (invoice.getDeleteFlag().equalsIgnoreCase("Y")) {
                System.out.println("**InvoiceAndReceiptBean: Invoice has been previously deleted.");
            } else {
                invoice.setDeleteFlag("Y");
                em.merge(invoice);
                em.flush();
                System.out.println("**InvoiceAndReceiptBean: Invoice has been successfully deleted.");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public List<InvoiceAndReceiptState> viewAllInvoice() {
        System.out.println("**InvoiceAndReceiptBean : viewAllInvoice()");

        Query q = em.createQuery("SELECT i FROM InvoiceAndReceiptEntity i");
        List<InvoiceAndReceiptState> stateList = new ArrayList<InvoiceAndReceiptState>();

        for (Object o : q.getResultList()) {
            InvoiceAndReceiptEntity i = (InvoiceAndReceiptEntity) o;

            if (i.getDeleteFlag().equalsIgnoreCase("N")) {
                InvoiceAndReceiptState cpState = new InvoiceAndReceiptState(i.getInvoiceId(), i.getSalesOrderId(), i.getCashInAdvance(), i.getDiscountRate(),
                        i.getTotalPrice(), i.getIssueDate(), i.getInvoiceDueDate(), i.getCreditTerm(),
                        i.getDiscountedCreditTerm(), i.getAmountDue(), i.isInvoiceStatus(), i.getDeleteFlag(), i.getiLineEntry(), i.getPaymentMethod());

                stateList.add(cpState);
            }
        }
        return stateList;
    }

    public List<InvoiceAndReceiptState> viewOverdueInvoice() {
        System.out.println("**InvoiceAndReceiptBean : viewOverdueInvoice()");

        boolean status = false;
        Query q = em.createQuery("SELECT i FROM InvoiceAndReceiptEntity i WHERE i.invoiceStatus = :status");
        q.setParameter("status", status);
        List<InvoiceAndReceiptState> stateList = new ArrayList<InvoiceAndReceiptState>();

        Calendar calendar = Calendar.getInstance();
        Date curDate = calendar.getTime();

        for (Object o : q.getResultList()) {
            InvoiceAndReceiptEntity i = (InvoiceAndReceiptEntity) o;

            if (i.getDeleteFlag().equalsIgnoreCase("N") && curDate.after(i.getInvoiceDueDate())) {
                InvoiceAndReceiptState cpState = new InvoiceAndReceiptState(i.getInvoiceId(), i.getSalesOrderId(), i.getCashInAdvance(), i.getDiscountRate(),
                        i.getTotalPrice(), i.getIssueDate(), i.getInvoiceDueDate(), i.getCreditTerm(),
                        i.getDiscountedCreditTerm(), i.getAmountDue(), i.isInvoiceStatus(), i.getDeleteFlag(), i.getiLineEntry(), i.getPaymentMethod());

                stateList.add(cpState);
            }
        }
        return stateList;
    }

    public List<InvoiceAndReceiptState> viewOutstandingInvoice() {
        System.out.println("**InvoiceAndReceiptBean : viewOustandingInvoice()");

        boolean status = false;
        Query q = em.createQuery("SELECT i FROM InvoiceAndReceiptEntity i WHERE i.invoiceStatus = :status");
        q.setParameter("status", status);
        List<InvoiceAndReceiptState> stateList = new ArrayList<InvoiceAndReceiptState>();

        for (Object o : q.getResultList()) {
            InvoiceAndReceiptEntity i = (InvoiceAndReceiptEntity) o;

            if (i.getDeleteFlag().equalsIgnoreCase("N")) {
                InvoiceAndReceiptState cpState = new InvoiceAndReceiptState(i.getInvoiceId(), i.getSalesOrderId(), i.getCashInAdvance(), i.getDiscountRate(),
                        i.getTotalPrice(), i.getIssueDate(), i.getInvoiceDueDate(), i.getCreditTerm(),
                        i.getDiscountedCreditTerm(), i.getAmountDue(), i.isInvoiceStatus(), i.getDeleteFlag(), i.getiLineEntry(), i.getPaymentMethod());

                stateList.add(cpState);
            }
        }
        return stateList;
    }

    public boolean updateInvoiceStatus(Long invoiceId, boolean invoiceStatus) {
        System.out.println("**InvoiceAndReceiptBean : updateInvoiceStatus()");
        
        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);

        try {
            invoice.setInvoiceStatus(invoiceStatus);
            System.out.println("**InvoiceAndReceiptBean: Invoice status has been successfully updated.");
            return true;
        } catch (Exception ex) {
            System.out.println("**InvoiceAndReceiptBean: Fail to update invoice status.");
            return false;
        }
    }

    public Double getCashInAdvanceAmt(Long salesOrderId) {
        System.out.println("**InvoiceAndReceiptBean: getCashInAdvanceAmt()");
        
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        Double paymentAsCIA;
        if (salesOrder.getPaymentMethod().equalsIgnoreCase("Cash")) {
            paymentAsCIA = salesOrder.getTotalPrice();
        } else {
            paymentAsCIA = 0.0;
        }
        return paymentAsCIA;
    }

    public String getCustomerName(Long salesOrderId) {
        System.out.println("**InvoiceAndReceiptBean: getCustomerName()");
        
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        String custName = salesOrder.getClient().getName();

        return custName;
    }

    public Collection<LineEntryEntity> getProductDetails(Long salesOrderId) {
        System.out.println("**InvoiceAndReceiptBean: getProductDetails()");
        
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        Collection<LineEntryEntity> productOrdered = salesOrder.getLineEntryList();
        productOrdered.size();

        return productOrdered;
    }

    public Double getTotalPrice(Long salesOrderId) {
        System.out.println("**InvoiceAndReceiptBean: getTotalPrice()");
        
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        Collection<LineEntryEntity> LineEntryList = getProductDetails(salesOrderId);

        Double amount = 0.0;
        if (salesOrder.getPricingType().equalsIgnoreCase("F")) {
            for (Object o : LineEntryList) {
                LineEntryEntity l = (LineEntryEntity) o;
                Double temp = l.getAmountInvoiced() * l.getPrice();
                amount = amount + temp;
            }
        } else if (salesOrder.getPricingType().equalsIgnoreCase("D")) {
            for (Object o : LineEntryList) {
                LineEntryEntity l = (LineEntryEntity) o;
                Double temp = l.getAmountInvoiced() * l.getPrice();
                amount = amount + temp;
            }
        }
        return amount;
    }

    public Collection<InvoiceLineEntryEntity> getInvoiceLineEntry(Long invoiceId) {
        System.out.println("**InvoiceAndReceiptBean: getInvoiceLineEntry()");

        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        Collection<InvoiceLineEntryEntity> iLineEntry = invoice.getiLineEntry();
        iLineEntry.size();
        return iLineEntry;
    }
    
    public List<String> getPaymentMethods(Long salesOrderId) {
        System.out.println("**InvoiceAndReceiptBean: getPaymentMethods()");
        
        SalesOrderEntity salesOrder = em.find(SalesOrderEntity.class, salesOrderId);
        Long custId = salesOrder.getClient().getId();
        
        Query q = em.createQuery("SELECT c FROM CreditProfileEntity c WHERE c.customerId = :status");
        q.setParameter("status", custId);
        CreditProfileEntity cp = (CreditProfileEntity) q.getSingleResult();
        List<String> methods = cp.getMethodOfPayment();
        
        return methods;
    }
    
    public String retrievePaymentMethod(Long invoiceId) {
        System.out.println("**InvoiceAndReceiptBean: retrievePaymentMethod()");
        
        InvoiceAndReceiptEntity invoice = em.find(InvoiceAndReceiptEntity.class, invoiceId);
        String method = invoice.getPaymentMethod();
        
        return method;
    }
}
